package org.aurora.common.util;

import com.alibaba.fastjson.JSONObject;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.message.BasicHeader;
import org.apache.hc.core5.http.message.BasicNameValuePair;
import org.aurora.common.exception.HttpRequestException;

import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class HttpClientUtil {

    private static volatile CloseableHttpAsyncClient HTTP_ASYNC_CLIENT = null;

    private HttpClientUtil() {}

    private static CloseableHttpAsyncClient getInstance() {
        if (HttpClientUtil.HTTP_ASYNC_CLIENT == null) {
            synchronized (HttpClientUtil.class) {
                if (HttpClientUtil.HTTP_ASYNC_CLIENT == null) {
                    HttpClientUtil.HTTP_ASYNC_CLIENT = HttpAsyncClients.createDefault();
                    HttpClientUtil.HTTP_ASYNC_CLIENT.start();
                }
            }
        }
        return HttpClientUtil.HTTP_ASYNC_CLIENT;
    }

    public static <T> T get(String url, Map<String, Object> params, Class<T> clazz, Long timeout, TimeUnit timeUnit) {
        return HttpClientUtil.get(url, null, params, clazz, timeout, timeUnit);
    }

    public static <T> T get(String url, Class<T> clazz, Map<String, Object> headers, Long timeout, TimeUnit timeUnit) {
        return HttpClientUtil.get(url, headers, null, clazz, timeout, timeUnit);
    }

    public static <T> T get(String url, Class<T> clazz, Long timeout, TimeUnit timeUnit) {
        return HttpClientUtil.get(url, null, null, clazz, timeout, timeUnit);
    }

    public static <T> T get(String url, Map<String, Object> headers, Map<String, Object> params, Class<T> clazz, Long timeout, TimeUnit timeUnit) {
        SimpleHttpRequest request = SimpleRequestBuilder
                .get(url)
                .setHeaders(MapUtils.isNotEmpty(headers) ? headers.entrySet().stream().map(entry -> new BasicHeader(entry.getKey(), entry.getValue())).toArray(Header[]::new) : new BasicHeader[0])
                .addParameters(MapUtils.isNotEmpty(params) ? params.entrySet().stream().map(entry -> new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue()))).toArray(BasicNameValuePair[]::new) : new BasicNameValuePair[0])
                .build();

        Future<SimpleHttpResponse> future = getInstance().execute(request, null);
        try {
            SimpleHttpResponse response = future.get(timeout, timeUnit);

            if (response == null) {
                throw new HttpRequestException("Response is null");
            }

            int code = response.getCode();
            String bodyText = response.getBodyText();

            if (!Objects.equals(code, HttpStatus.SC_SUCCESS)) {
                throw new HttpRequestException(code, String.format("Response is fail, code: %s, response: %s", code, bodyText));
            }

            if (StringUtils.isEmpty(bodyText)) {
                return null;
            }

            if (clazz == String.class) {
                return (T) bodyText;
            } else if (clazz == Byte.class || clazz == byte.class) {
                return (T) Byte.valueOf(bodyText);
            } else if (clazz == Short.class || clazz == short.class) {
                return (T) Short.valueOf(bodyText);
            } else if (clazz == Integer.class || clazz == int.class) {
                return (T) Integer.valueOf(bodyText);
            } else if (clazz == Long.class || clazz == long.class) {
                return (T) Long.valueOf(bodyText);
            } else if (clazz == Float.class || clazz == float.class) {
                return (T) Float.valueOf(bodyText);
            } else if (clazz == Double.class || clazz == double.class) {
                return (T) Double.valueOf(bodyText);
            } else if (clazz == Character.class || clazz == char.class) {
                return (T) Character.valueOf(bodyText.charAt(0));
            } else if (clazz == Boolean.class || clazz == boolean.class) {
                return (T) Boolean.valueOf(bodyText);
            } else {
                return JSONObject.parseObject(bodyText, clazz);
            }
        } catch (HttpRequestException e) {
            throw e;
        } catch (Exception e) {
            throw new HttpRequestException(e);
        }
    }
}
